package idoc.index;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;

public class DataBlockLink {

	private RandomAccessFile f;
	
	
	private int c;
	private int hs;

	public DataBlockLink(String file, int blocakSize, int blockHeadSize)
			throws FileNotFoundException {

		f = new RandomAccessFile(file, "rw");
		c = blocakSize;
		hs = blockHeadSize;
		
	}

	public DataBlock create() throws IOException {

		return new DataBlock( this);

	}
	
	public DataBlock get(long p) throws IOException{
		return new DataBlock(f.length(), this);
	}
	public static class DataBlock {

		private static final int N_PT_IDX = 4;
		private static final int U_IDX = 0;
		private long nextPointer;
		private int use;
		private DataBlockLink ctx;
		private ByteBuffer buffer;
		private long pointer;
		private int headSize;
		private int rdIdx;

		// init data block
		public DataBlock(DataBlockLink ctx) throws IOException {

			this.ctx = ctx;
			pointer = ctx.f.length();
			nextPointer = -1L;
			use = headSize;
			buffer = ByteBuffer.allocate(ctx.c);
			buffer.putInt(use);
			buffer.putLong(nextPointer);
			rdIdx = headSize;
			write(ctx.f, pointer, buffer.array());

		}

		public DataBlock(long p, DataBlockLink ctx) throws IOException {

			this.ctx = ctx;
			this.pointer = p;
			this.buffer = ByteBuffer.allocate(ctx.c);
			byte[] bs = new byte[ctx.c];
			ctx.f.seek(p);
			ctx.f.read(bs);
			buffer.put(bs);
			this.nextPointer = buffer.getLong(N_PT_IDX);
			rdIdx = headSize;

		}

		public DataBlock write(byte[] bs) throws IOException {
			DataBlock d = this;
			for (byte b : bs) {
				d = d.write(b);
			}
			return d;
		}

		public DataBlock writeInt(int i) throws IOException {

			byte[] bs = tobytes(i);
			return write(bs);

		}

		private DataBlock next() throws IOException {
			DataBlock block = new DataBlock(nextPointer, ctx);
			return block;
		}

		public int read(byte[] bs, int off, int len) throws IOException {

			if (rdIdx == use) {
				if (nextPointer > 0) {
					// DataBlock block = new DataBlock(nextPointer, ctx);
					return next().read(bs, off, len);
				}
				return -1;
			}
			if (len <= use - rdIdx) {
				buffer.get(bs, off, len);
				return len;
			} else {
				int _len = len - rdIdx;
				buffer.get(bs, off, _len);
				return next().read(bs, off + _len, len - _len);
			}

		}

		public int read(byte[] bs) throws IOException {

			return read(bs, 0, bs.length);

		}

		public int readInt() throws IOException {

			byte[] bs = new byte[4];
			int r = read(bs);
			if (r < 0) {
				return r;
			} else {
				return toInt(bs);
			}
		}
		
		
		public Long readLong() throws IOException {
			byte[] bb = new byte[8];
			if(read(bb)==-1)return null;
			return ((((long) bb[0] & 0xff) << 56)
			        | (((long) bb[1] & 0xff) << 48)
			        | (((long) bb[2] & 0xff) << 40)
			        | (((long) bb[3] & 0xff) << 32)
			        | (((long) bb[4] & 0xff) << 24)
			        | (((long) bb[5] & 0xff) << 16)
			        | (((long) bb[6] & 0xff) << 8) | (((long) bb[7] & 0xff) << 0));
		} 
		
		private int free() {
			return buffer.limit() - use;
		}

		private byte[] tobytes(int val) {
			byte[] bs = new byte[4];
			for (int i = 0; i < bs.length; i++) {
				bs[i] = (byte) (val << bs.length - i - 1);
			}
			return bs;
		}

		public static int toInt(byte[] b) {

			int mask = 0xff;
			int temp = 0;
			int n = 0;
			for (int i = 0; i < 4; i++) {
				n <<= 8;
				temp = b[i] & mask;
				n |= temp;
			}
			return n;

		}

		private byte[] tobytes(long val) {
			byte[] bs = new byte[8];
			for (int i = 0; i < bs.length; i++) {
				bs[i] = (byte) (val << bs.length - i - 1);
			}
			return bs;
		}

		public DataBlock write(byte val) throws IOException {

			if (free() > 0) {
				buffer.put(use++, val);
				buffer.putInt(U_IDX, use);
				return this;
			} else {
				long p = ctx.f.length();
				DataBlock b = ctx.create();
				this.nextPointer = p;
				buffer.putLong(N_PT_IDX, nextPointer);
				return b.write(val);
			}

		}

		public DataBlock writeLong(long val) throws IOException {
			byte[] bs = tobytes(val);
			return this.write(bs);
		}

		private void flush() throws IOException {

			write(ctx.f, pointer, buffer.array());

		}

		private synchronized static void write(RandomAccessFile f, long p,
				byte[] bs) throws IOException {

			f.seek(p);
			f.write(bs);

		}

		public long pointer() {
			// TODO Auto-generated method stub
			return this.pointer;
		}

	}

	public static void main(String[] args) throws IOException {

		DataBlockLink file = new DataBlockLink("1.dat", 120, 20);
		DataBlock block = file.create();
		long t1 = System.currentTimeMillis();
		for (int i = 0; i < 1000; i++) {
			block = block.write("abc".getBytes());
		}
		block = file.get(0L);
	
		long t2 = System.currentTimeMillis();
		System.out.println(t2 - t1);

	}
}
